package lk.icta.user.entity;

import lk.icta.common.entity.Branch;
import lk.icta.common.entity.DSDivision;
import lk.icta.common.entity.District;
import lk.icta.core.entity.LifeCycleInfo;
import lk.icta.core.entity.Organization;
import org.codehaus.jackson.annotate.JsonBackReference;
import org.codehaus.jackson.annotate.JsonManagedReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.persistence.*;
import java.util.Date;
import java.util.Set;

/**
 * @author Mahesha Kalpanie
 */
@Entity
@Table(schema = "USERS_RAIL")
@NamedQueries({
        @NamedQuery(name = "getAllUser", query = "SELECT u FROM User u "),
        @NamedQuery(name = "getUserByEmail", query = "SELECT u FROM User u WHERE u.email = :email"),
        @NamedQuery(name = "getUsersByOrganization", query = "SELECT u FROM User u WHERE u.organization.id = :organizationId "),
        @NamedQuery(name = "getORPIIUsersByOrganization", query = "SELECT u FROM User u WHERE u.organization.id = :organizationId AND u.role.id = :roleId "),
        @NamedQuery(name = "getUsersByRole", query = "SELECT u FROM User u WHERE u.status = :state AND u.role.id = :roleId "),
        @NamedQuery(name = "getNewURMGOIUsersByOrganization", query = "SELECT u FROM User u WHERE u.organization.id = :organizationId AND u.role.id = :roleId AND u.status = :state"),
        @NamedQuery(name = "getUserByUserName", query = "SELECT u FROM User u WHERE u.username = :username "),
        @NamedQuery(name = "getUsersByRoleDS", query = "SELECT u FROM User u WHERE u.status = :state AND u.role.id = :roleId AND :dsId MEMBER OF u.dsDivisions"),
        @NamedQuery(name = "getUsersByRoleProvince", query = "SELECT DISTINCT u FROM User u JOIN u.dsDivisions ds WHERE u.status = :state AND u.role.id = :roleId AND ds.district.province.id = :provinceId "),
        @NamedQuery(name = "getAllUsersByPA", query = "SELECT DISTINCT u FROM User u JOIN u.dsDivisions ds WHERE u.status = :state AND u.role.id <> 'PLC' AND ds.district.province.id = :provinceId")
})
public class User {

    public enum LoginState {
        NORMAL, // 0 - Users who has changed the password at first login
        FIRST_LOGIN, //1 - state of the user once created (before changing the password by user)
        PASSWORD_RESET //2 - password has been reset by admin (require to change password at nest login)
    }

    public enum State {
        INACTIVE, /**
         * 0 - state. Cannot login
         */
        ACTIVE, /**
         * 1 - state. Can login
         */
        LOCKEDOUT, /**
         * 2 - state. Cannot login
         */
        DELETED /** 3 - state. Permanently deleted cannot be edited or login */
    }

    private static final Logger logger = LoggerFactory.getLogger(User.class);


    /*  */
    /**
     * This is an auto generated unique row identifier
     *//*
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private long id;*/


    @Id
    @Column(updatable = false, length = 30)
    private String username;

    /**
     * The Full name of the user
     */
    @Column(nullable = false)
    private String fullName;

    /**
     * The email address of the user
     */
    @Column(length = 100)
    private String email;

    /**
     * The Person  Address
     */
    @Column(nullable = true)
    private String address;

    /**
     * Land phone number of The Person
     */
    @Column(nullable = true)
    private String landPhone;

    /**
     * Mobile number of The Person
     */
    @Column(nullable = true)
    private String mobile;

    /**
     * The SHA-1 hash of the password
     */
    @Column(length = 60)
    private String passwordHash;

    /**
     * The preferred language
     */
    @Column(length = 2, nullable = false)
    private String prefLanguage;

    /**
     * The password expiry date, after which the user is not allowed to login without changing the password
     */
    @Column(nullable = true)
    private Date passwordExpiryDate;

    /**
     * The assigned DS Divisions for the user
     */
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(schema = "COMMON_RAIL")
    private Set<DSDivision> dsDivisions;

    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(schema = "USERS_RAIL")
    private Set<Role> roles;

    @Column(nullable = false, columnDefinition = "smallint not null default 1")
    private State status;

    @ManyToOne(optional = false, fetch = FetchType.EAGER)
    @JoinColumn
    private Role role;

    @ManyToOne
    @JoinColumn(nullable = true)
    private Organization organization;

    @Column(nullable = false)
    private LoginState loginState;

    /**
     * The assigned Districts for the user
     */
    @ManyToMany(fetch = FetchType.EAGER)
    @JoinTable(schema = "COMMON_RAIL")
    private Set<District> districts;

    @ManyToOne
    @JoinColumn(nullable = true)
    private Branch branch;

    @Transient
    private String userDevice;

    @Embedded
    private LifeCycleInfo lifeCycleInfo = new LifeCycleInfo();

    public User() {
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        User user = (User) o;
        if (username != null ? !username.equals(user.username) : user.username != null) {
            return false;
        }
        return true;
    }

    @Override
    public int hashCode() {
        return username != null ? username.hashCode() : 0;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getFullName() {
        return fullName;
    }

    public void setFullName(String fullName) {
        this.fullName = fullName;
    }

    public String getPasswordHash() {
        return passwordHash;
    }

    public void setPasswordHash(String passwordHash) {
        this.passwordHash = passwordHash;
    }

    public String getPrefLanguage() {
        return prefLanguage;
    }

    public void setPrefLanguage(String prefLanguage) {
        this.prefLanguage = prefLanguage;
    }

    public State getStatus() {
        return status;
    }

    public void setStatus(State status) {
        this.status = status;
    }

    public Role getRole() {
        return role;
    }

    public void setRole(Role role) {
        this.role = role;
    }

    public Date getPasswordExpiryDate() {
        return passwordExpiryDate;
    }

    public void setPasswordExpiryDate(Date passwordExpiryDate) {
        this.passwordExpiryDate = passwordExpiryDate;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Set<DSDivision> getDsDivisions() {
        return dsDivisions;
    }

    public void setDsDivisions(Set<DSDivision> dsDivisions) {
        this.dsDivisions = dsDivisions;
    }

    public boolean isAuthorized(int permission) {
        return role == null ? false : role.getPermBitSet().get(permission);
    }

    public Organization getOrganization() {
        return organization;
    }

    public void setOrganization(Organization organization) {
        this.organization = organization;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }


    public String getLandPhone() {
        return landPhone;
    }

    public void setLandPhone(String landPhone) {
        this.landPhone = landPhone;
    }


    public String getMobile() {
        return mobile;
    }

    public void setMobile(String mobile) {
        this.mobile = mobile;
    }

    @JsonBackReference("user-lifecycle")
    public LifeCycleInfo getLifeCycleInfo() {
        return lifeCycleInfo;
    }

    public void setLifeCycleInfo(LifeCycleInfo lifeCycleInfo) {
        this.lifeCycleInfo = lifeCycleInfo;
    }

    public Set<Role> getRoles() {
        return roles;
    }

    public void setRoles(Set<Role> roles) {
        this.roles = roles;
    }

    public LoginState getLoginState() {
        return loginState;
    }

    public void setLoginState(LoginState loginState) {
        this.loginState = loginState;
    }

    public String getUserDevice() {
        return userDevice;
    }

    public void setUserDevice(String userDevice) {
        this.userDevice = userDevice;
    }

    public Set<District> getDistricts() {
        return districts;
    }

    public void setDistricts(Set<District> districts) {
        this.districts = districts;
    }

    public Branch getBranch() {
        return branch;
    }

    public void setBranch(Branch branch) {
        this.branch = branch;
    }
}